/*
********************************************************************************
* Copyright (C) 2021, xiang.D <dx_65535@163.com>.
* All right reserved.
*
* File Name   : state.cc
* Author      :
* Version     : V1.0
* Description :
* Journal     : 2021-12-17 version v1.0
* Brief       : Blog: https://blog.csdn.net/qq_38750572?spm=1001.2014.3001.5343
* Others      :
                Compile command: make
********************************************************************************
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "elevator_srv.h"

#define ELEVATORSRV_LOGI(fmt, args...) printf("%d ElevatorSrv I: " fmt, __LINE__, ##args)
#define ELEVATORSRV_LOGD(fmt, args...)
#define ELEVATORSRV_LOGW(fmt, args...) printf("%d ElevatorSrv W: " fmt, __LINE__, ##args)
#define ELEVATORSRV_LOGE(fmt, args...) printf("%d ElevatorSrv E: " fmt, __LINE__, ##args)
#define ELEVATORSRV_LOG(fmt, args...)  printf(fmt, ##args)

using namespace std;

StateTransition <EElevatorDoorState, EElevatorRunState, EMsgType, CElevatorSrv, SMsgPacket>
CElevatorSrv::mStateTable[] =
{
    { LEV1_ANY,         LEV2_ANY,   SIG_ID_POWER_ON,        &CElevatorSrv::MsgRespondInit},
    { LEV1_DOOR_OPEN,   LEV2_IDLE,  SIG_ID_POWER_OFF,       &CElevatorSrv::MsgRespondShutdown},
    { LEV1_DOOR_CLOSE,  LEV2_IDLE,  SIG_ID_TAKE_UP_ORDER,   &CElevatorSrv::MsgRespondOrderUpIdle},
    { LEV1_ANY,         LEV2_ANY,   SIG_ID_TAKE_UP_ORDER,   &CElevatorSrv::MsgRespondOrderUp},
    { LEV1_DOOR_CLOSE,  LEV2_IDLE,  SIG_ID_TAKE_DOWN_ORDER, &CElevatorSrv::MsgRespondOrderDownIdle},
    { LEV1_ANY,         LEV2_ANY,   SIG_ID_TAKE_DOWN_ORDER, &CElevatorSrv::MsgRespondOrderDown},
    { LEV1_ANY,         LEV2_ANY,   SIG_ID_ARRIVE_FLOOR,    &CElevatorSrv::MsgRespondArriveFloor},
    { LEV1_ANY,         LEV2_IDLE,  SIG_ID_EXIT,            &CElevatorSrv::MsgRespondExit},
    { LEV1_ANY,         LEV2_ANY,   SIG_ID_ANY,             &CElevatorSrv::MsgRespondIgnore}
};

const char LEV2_SHOW[][MAX_STRING] = LEV2_STRING;

CElevatorSrv::CElevatorSrv()
{

}

CElevatorSrv::~CElevatorSrv()
{

}

void CElevatorSrv::Init()
{
    // 初始化成员变量
    ELEVATORSRV_LOGI("--- Elevator Init --- \n");
    mIsFull = false;
    mQuit   = false;
    mStart  = false;
    mCurrentFloor = MIN_FLOOR;
    mCurLev1State = LEV1_DOOR_CLOSE;
    mCurLev2State = LEV2_IDLE;
    memset(mArriveTable,    0x00, sizeof(mArriveTable));
    memset(mOrderUpTable,   0x00, sizeof(mOrderUpTable));
    memset(mOrderDownTable, 0x00, sizeof(mOrderDownTable));

    // 程序运行
    Run();
}

void thread_run(void *rhs)
{
    EElevatorRunState state;

    if (nullptr == rhs) {
        ELEVATORSRV_LOGE("Invaild Params!\n");
        return;
    } else {
        ELEVATORSRV_LOGI("Start thread_run!\n");
    }
    CElevatorSrv *ths = (CElevatorSrv*)rhs;
    ths->SetStart(true);

    do {
        // 电梯开门的几个条件:
        // 1. 当前楼层, 有人要下
        // 2. 当前楼层，有人要上。运行方向与上行/下行表匹配 mOrderUpTable、mOrderDownTable
        int floor = ths->GetCurrentFloor();
        ths->CloseDoor();                                   // 运行关门
        sleep(5);                                           // 移动一层时间
        state = ths->GetLev2State();
        ELEVATORSRV_LOG("Current %d floor State %s\n",
                    ths->GetCurrentFloor(), LEV2_SHOW[ths->GetLev2State()]);
        if (state == LEV2_UP)                               // 上行中
        {
            if (   ths->GetCurrentFloorUpRequire()          // 上行, 当前楼层存在向上需求，开门
                || ths->IsArrivalFloor()
               )
            {
                ELEVATORSRV_LOGI("-->Floor: %d. Open Door!\n", ths->GetCurrentFloor());
                ths->OpenDoor();
                ths->ClearOrderUp(floor);                   // 清理当前楼层向上预定
                ths->ClearArrival(floor);                   // 清理预定到达当前楼层表
                ths->CloseDoor();                           // 关门
            }

            if (   ths->GetOrderHighestFloor() <= MAX_FLOOR // 高层还存在上行/下行需求, 还需要上行
                && floor < ths->GetOrderHighestFloor()
               )
            {
                floor++;
                ths->SetCurrentFloor(floor);
            } else {
                //ths->SetOrderHighestFloor(MAX_FLOOR + 1);  // 已到达最高需求楼层, 重新置位需求最高楼层

                if (ths->ExistRequire()) {
                    ths->UpdateLev2State(LEV2_DOWN);        // 达到最高需求楼层, 还存在需求, 需要下行
                } else {
                    ths->UpdateLev2State(LEV2_IDLE);
                }
            }

        }
        else if (state == LEV2_DOWN)                        // 下行中
        {
            if (   ths->GetCurrentFloorDownRequire()        // 下行, 当前楼层存在向下需求，开门
                || ths->IsArrivalFloor()
               )
            {
                ELEVATORSRV_LOGI("-->Floor: %d. Open Door!\n", ths->GetCurrentFloor());
                ths->OpenDoor();                            // 关门
                ths->ClearOrderDown(floor);                 // 清理当前楼层向下预定
                ths->ClearArrival(floor);                   // 清理预定到达当前楼层表
                ths->CloseDoor();
            }

            if (   ths->GetOrderShortestFloor() >= MIN_FLOOR
                && floor > ths->GetOrderShortestFloor()     // 底层还存在上行/下行需求, 还需要下行
               )
            {
                floor--;
                ths->SetCurrentFloor(floor);
            } else {
                //ths->SetOrderShortestFloor(MIN_FLOOR - 1);  // 当前为需求最底层, 不再需要下行

                if (ths->ExistRequire()) {
                    ths->UpdateLev2State(LEV2_UP);
                } else {
                    ths->UpdateLev2State(LEV2_IDLE);
                }
            }
        } else {

        }
    } while(!ths->IsQuit());

    return ;
}

void CElevatorSrv::Run()
{
    mRunTid = thread(thread_run, (void *)this);
}

void CElevatorSrv::Stop()
{
    mRunTid.join();
    ELEVATORSRV_LOG("Current floor %d State %d\n", GetCurrentFloor(), GetLev2State());
}

void CElevatorSrv::AddOrderTable(STakeOrderInfo *pInfo)
{
    if (nullptr == pInfo || pInfo->floor > MAX_FLOOR || pInfo->floor < MIN_FLOOR) {
        ELEVATORSRV_LOGE("Invaild Params!\n");
    }

    STakeOrderInfo *p = pInfo;
    if (p->floor < GetOrderShortestFloor()) {       // 更新楼层需求范围
        SetOrderShortestFloor(p->floor);
    } else if (p->floor > GetOrderHighestFloor()) {
        SetOrderHighestFloor(p->floor);
    } else {

    }

    if (p->direct == TAKE_UP) {                     // 登记预约乘坐
        mOrderUpTable[p->floor] = ORDER_MARK;
    } else {
        mOrderDownTable[p->floor] = ORDER_MARK;
    }
}

void CElevatorSrv::AddArriveTable(int floor)
{
    if (floor < GetOrderShortestFloor()) {       // 更新楼层需求范围
        SetOrderShortestFloor(floor);
    } else if (floor > GetOrderHighestFloor()) {
        SetOrderHighestFloor(floor);
    } else {

    }

    mArriveTable[floor] = ORDER_MARK;
}

void CElevatorSrv::UpdateRequireRange()
{
    int i = 0, sum = 0;

    SetOrderHighestFloor(MAX_FLOOR + 1);        // 置位需求范围
    SetOrderShortestFloor(MIN_FLOOR - 1);

    //mRequireTable.clear();
    for (i = 0; i < MAX_FLOOR; i++) {
        sum = mArriveTable[i] + mOrderUpTable[i] + mOrderDownTable[i];
        if (sum != 0) {
            //mRequireTable.push_back(i);       // 保存当前存在预约的楼层
            if (i > GetOrderHighestFloor() || GetOrderHighestFloor() > MAX_FLOOR) {   // 更新最高楼层
                SetOrderHighestFloor(i);
            }

            if (i < GetOrderShortestFloor() || GetOrderShortestFloor() < MIN_FLOOR) {  // 更新最低楼层
                SetOrderShortestFloor(i);
            }
        }
    }
}

bool CElevatorSrv::ExistRequire()
{
    UpdateRequireRange();

    if (    GetOrderHighestFloor() <= MAX_FLOOR // 剩一个需求时，最高层与最底层一致，只需判断其中一个
         && GetOrderHighestFloor() >= MIN_FLOOR
        )
    {
        ELEVATORSRV_LOGD("Highest %d Shortest %d\n", GetOrderHighestFloor(), GetOrderShortestFloor());
        return true;
    } else {
        return false;
    }
}

void CElevatorSrv::ShowOrderTable(int min, int max)
{
    if (min < MIN_FLOOR || MAX_FLOOR > MAX_FLOOR || min > max) {
        ELEVATORSRV_LOGE("Invaild Params!\n");
    }
    for (int i = min; i <= max; i++) {
        ELEVATORSRV_LOG("Floor %d: Up[%d] Down[%d] Arrive[%d] \n",
                        i, mOrderUpTable[i], mOrderDownTable[i], mArriveTable[i]);
    }
}

int CElevatorSrv::GetOrderShortestFloor()
{
    return mOrderShortestFloor;
}

CElevatorSrv* CElevatorSrv::GetInstance()
{
    static CElevatorSrv mInstance;
    return &mInstance;
}

/* 发送到ProcessMsg消息，供外部调用 */
void CElevatorSrv::SendCmd(SMsgPacket *pMsg)
{
    // TODO: 加锁保护
    ProcessMsg(pMsg);
}

/* 更新电梯一级状态 */
void CElevatorSrv::CElevatorSrv::UpdateLev1State(EElevatorDoorState state)
{
    ELEVATORSRV_LOGD("Lev1State: 0x%x -> 0x%x\n", GetLev1State(), state);
    mCurLev1State = state;
}

/* 更新电梯二级状态 */
void CElevatorSrv::UpdateLev2State(EElevatorRunState state)
{
    ELEVATORSRV_LOGD("Lev2State: %s -> %s\n", LEV2_SHOW[GetLev2State()], LEV2_SHOW[state]);
    mCurLev2State = state;
}

bool CElevatorSrv::IsArrivalFloor()
{
    int floor = GetCurrentFloor();
    return mArriveTable[floor] == ORDER_MARK ? true : false;
}

bool CElevatorSrv::GetCurrentFloorUpRequire()
{
    int floor = GetCurrentFloor();
    return mOrderUpTable[floor] == ORDER_MARK ? true : false;
}

bool CElevatorSrv::GetCurrentFloorDownRequire()
{
    int floor = GetCurrentFloor();
    return mOrderDownTable[floor] == ORDER_MARK ? true : false;
}

int CElevatorSrv::GetOrderHighestFloor()
{
    //UpdateRequireRange();
    return mOrderHighestFloor;
}

/* 响应消息 */
void CElevatorSrv::ProcessMsg(SMsgPacket *pMsg)
{
    if (!IsStart()) {
        ELEVATORSRV_LOGE("Elevator not start!\n");
        return;
    }

    if (pMsg == nullptr) {
        ELEVATORSRV_LOGE("pMsg is nullptr!\n");
        return;
    }

    int index = 0;
    EElevatorDoorState curLev1State = GetLev1State();
    EElevatorRunState  curLev2State = GetLev2State();
    EMsgType msgId = pMsg->type;

    ELEVATORSRV_LOGD("Get Msg: 0x%x\n", msgId);
    // loop: 遍历状态表，进入与状态匹配的入口
    do
    {
        if (   (   (mStateTable[index].lev1State  == curLev1State)
                || (mStateTable[index].lev1State  == LEV1_ANY)
               )
            && (   (mStateTable[index].lev2State  == curLev2State)
                || (mStateTable[index].lev2State  == LEV2_ANY)
               )
            && (   (mStateTable[index].msgId      == msgId)
                || (mStateTable[index].msgId      == SIG_ID_ANY)
               )
           )
        {
            (this->*(mStateTable[index].callback))(pMsg);
                break;
        }
        index++;
    } while (1);
}

void CElevatorSrv::PowerOn(SMsgPacket *pMsg)
{

}

void CElevatorSrv::PowerOff(SMsgPacket *pMsg)
{

}

void CElevatorSrv::MoveUp(SMsgPacket *pMsg)
{

}

void CElevatorSrv::MoveDown(SMsgPacket *pMsg)
{

}
void CElevatorSrv::OpenDoor()
{
    if (GetLev1State() != LEV1_DOOR_OPEN) {
        UpdateLev1State(LEV1_DOOR_OPEN);
        sleep(3);
    }
}

void CElevatorSrv::CloseDoor()
{
    if (GetLev1State() != LEV1_DOOR_CLOSE) {
        UpdateLev1State(LEV1_DOOR_CLOSE);
        sleep(3);
    }
}

void CElevatorSrv::Idle(SMsgPacket *msg)
{

}

void CElevatorSrv::MsgRespondInit(SMsgPacket *pMsg)
{
    // 开机初始化
    Init();
}

void CElevatorSrv::MsgRespondShutdown(SMsgPacket *pMsg)
{
    SetQuit(true);
    Stop();
}

void CElevatorSrv::MsgRespondOrderUpIdle(SMsgPacket *pMsg)
{
    if (nullptr == pMsg->content) {
        ELEVATORSRV_LOGE("Invaild Params!\n");
        return;
    }

    STakeOrderInfo *p = (STakeOrderInfo *)pMsg->content;
    ELEVATORSRV_LOGI("Order Up From %d Floor!\n", p->floor);
    UpdateLev1State(LEV1_DOOR_CLOSE);
    UpdateLev2State(LEV2_UP);
    AddOrderTable(p);
}

void CElevatorSrv::MsgRespondOrderUp(SMsgPacket *pMsg)
{
    if (nullptr == pMsg->content) {
        ELEVATORSRV_LOGE("Invaild Params!\n");
        return;
    }

    STakeOrderInfo *p = (STakeOrderInfo *)pMsg->content;
    AddOrderTable(p);
    ELEVATORSRV_LOGI("Order Up From %d Floor!\n", p->floor);
}

void CElevatorSrv::MsgRespondOrderDownIdle(SMsgPacket *pMsg)
{
    if (nullptr == pMsg->content) {
        ELEVATORSRV_LOGE("Invaild Params!\n");
        return;
    }

    STakeOrderInfo *p = (STakeOrderInfo *)pMsg->content;
    ELEVATORSRV_LOGI("Order Down From %d Floor!\n", p->floor);

    UpdateLev1State(LEV1_DOOR_CLOSE);
    UpdateLev2State(LEV2_DOWN);
    AddOrderTable(p);
}

void CElevatorSrv::MsgRespondArriveFloor(SMsgPacket *pMsg)
{
    if (nullptr == pMsg->content) {
        ELEVATORSRV_LOGE("Invaild Params!\n");
        return;
    }

    int* p = (int *)pMsg->content;
    ELEVATORSRV_LOGI("Arrive floor: %d\n", *p);
    AddArriveTable(*p);
}

void CElevatorSrv::MsgRespondOrderDown(SMsgPacket *pMsg)
{
    if (nullptr == pMsg->content) {
        ELEVATORSRV_LOGE("Invaild Params!\n");
        return;
    }

    STakeOrderInfo *p = (STakeOrderInfo *)pMsg->content;
    AddOrderTable(p);
    ELEVATORSRV_LOGI("Order Down From %d floor!\n", p->floor);
}

void CElevatorSrv::MsgRespondExit(SMsgPacket *pMsg)
{

}

void CElevatorSrv::MsgRespondIgnore(SMsgPacket *pMsg)
{
    EElevatorDoorState curLev1State = GetLev1State();
    EElevatorRunState  curLev2State = GetLev2State();
    EMsgType msgId = pMsg->type;

    ELEVATORSRV_LOGE("Lev1 State: 0x%x, Lev2 State: 0x%x, MsgId: 0x%x. No Answer\n",
                  curLev1State, curLev2State, msgId);
}
